unit ProcessUtils;

interface

uses
  SysUtils, TlHelp32, Classes, Forms, windows, CodesiteLogging;


Function RunProgramAndWait(aCmd:String):Boolean;
Function ExecuteProgram(Cmd:String): Boolean;
function GetExecutableFromPID(dwProcessID: DWord): string; stdcall;

implementation

uses
  Messages;

type TRunThread = class(TThread)
  private
    processHandle : cardinal;
    processReady  : boolean;
    waitingThread : cardinal;
    procedure Execute; override;
  end;

function GetExecutableFromPID(dwProcessID: DWord): string; stdcall;
var 

  FSnapshotHandle: THandle;
  FModuleEntry32 : TModuleEntry32;
begin
  Result := '';
  Try
  if (dwProcessID <> 0) then begin
    FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,dwProcessID);
       if (fSnapshotHandle <> 0) then  begin
           FModuleEntry32.dwSize := Sizeof(FModuleEntry32);
           Module32First(FSnapshotHandle,FModuleEntry32);
           Result := FModuleEntry32.szExePath;
           CloseHandle(FSnapshotHandle);
        end;
   end;
  Except on E:Exception do
    begin
      CodeSite.SendWarning('ProcessUtils.GetExecutableFromPID  %s', [ e.Message ]);
      Result := '';
    end;
  End;
end;


Function ExecuteProgram(Cmd:String): Boolean;
var
  si: TStartupInfo;
  pi: TProcessInformation;
begin
  ZeroMemory(@si, SizeOf(si));
  si.cb := SizeOf(si);
  si.wShowWindow := SW_SHOW;
  ZeroMemory(@pi, SizeOf(pi));
  if CreateProcess(nil, PChar(Cmd), nil, nil, false, 0, nil, nil, si, pi) = false then Result := True
  else Result := False;
end;


Function RunProgramAndWait(aCmd:String):Boolean;
var si : TStartupInfo;
    pi : TProcessInformation;
    cmd: String;
begin
  Result := False;

  ZeroMemory(@si,sizeOf(si));
  si.cb:=sizeOf(si);
  si.dwFlags:=STARTF_USESHOWWINDOW;
  si.wShowWindow:=SW_HIDE;

  if CreateProcess(nil,pChar(aCmd),nil,nil,false,0,nil,nil,si,pi) then
  begin
    with TRunThread.Create(true) do         // create the thread object, but do not start it now...
      try
        processHandle:=pi.hProcess;         // tell the thread what process it has to watch
        processReady:=false;                // flag for the loop (see below)
        waitingThread:=GetCurrentThreadID;  // the current threadID for the wakeup message (see above)
        Resume;                             // now all information is prepared; so let's start the thread
        repeat
          Application.HandleMessage;        // message loop
        until Application.Terminated or processReady;  // continue with normal program when either the
                                                       // started process has stopped or our program is closed
      finally
        Free
      end;
    CloseHandle(pi.hThread); CloseHandle(pi.hProcess); // Never forget to close handles...
    Result := True;
  end
  else
  begin
    Result := False;
  end;

end;
  


{ TRunThread }

procedure TRunThread.Execute;
begin
  inherited;
  WaitForSingleObject(processHandle,INFINITE);   // This call does not return, unless copy is stopped
  processReady:=true;                            // Set "processReady" flag for main thread
  PostThreadMessage(waitingThread,WM_NULL,0,0);  // Wake up main thread
                                                 // If you call Application.HandleMessage (see below) in the
                                                 // main thread, the main thread is sleeping the most time in
                                                 // winAPI "waitMessage". So we send a "dummy" message in order
                                                 // to let the main thread return from Application.HandleMessage
end;


end.
